﻿using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System;
using System.Reflection;
using QxFramework.Utilities;

//继承自EditorWindow类
public class MyFirstWindow : EditorWindow
{
    Tree<RegisteredData> tree => Instance.Instance.tree;
    Vector2 scrolledPos;
    bool save = false;

    MyFirstWindow()
    {
        titleContent = new GUIContent("填数据");
    }

    //添加菜单栏用于打开窗口
    /*
    [MenuItem("Tools/填数据")]
    static void ShowWindow()
    {
        EditorWindow.GetWindow(typeof(MyFirstWindow));
    }
    */
    void OnGUI()
    {
        scrolledPos = EditorGUILayout.BeginScrollView(scrolledPos);
        EditorGUILayout.BeginVertical();
        if (GUILayout.Button("Refresh"))
        {
            Load();
        }
        EditorGUILayout.EndVertical();
        DrawFoldoutMenu(tree);
        EditorGUILayout.EndScrollView();
    }

    private void Load()
    {
        tree.RemoveAll();
        foreach (var gameObject in Resources.LoadAll<GameObject>(""))
        {
            ComponentsInGameObject componentsInGameObject = new ComponentsInGameObject(gameObject.name + "(GameObject)");
            List<Component> components = new List<Component>();
            gameObject.GetComponentsInChildren(true, components);
            foreach (var component in components)
            {
                bool have = false;
                FieldsInComponent fieldsInComponent = new FieldsInComponent(component.GetType().Name + "(Component)", component);
                foreach (var field in component.GetType().GetFields())
                {
                    if (Attribute.IsDefined(field, typeof(RegisterProperty)))
                    {
                        have = true;
                        fieldsInComponent.Add(field);
                        string[] paths = ((RegisterProperty)field.GetCustomAttribute(typeof(RegisterProperty))).Path.Split('/');
                        string _path = "";
                        for (int i = 0; i < paths.Length; i++)
                        {
                            if (tree.GetTree(_path + "/" + paths[i]) == null)
                            {
                                tree.GetTree(_path).AddNode(new Tree<RegisteredData>(paths[i]));
                            }
                            if (i == paths.Length - 1)
                            {
                                if (tree.GetTree(_path + "/" + paths[i]).Data == null)
                                {
                                    tree.GetTree(_path + "/" + paths[i]).Data = new RegisteredData();
                                }
                                tree.GetTree(_path + "/" + paths[i]).Data.Add(componentsInGameObject);
                            }
                            if (i != 0)
                            {
                                _path += "/";
                            }
                            _path += paths[i];
                        }
                    }
                }
                if (have)
                {
                    componentsInGameObject.Add(fieldsInComponent);
                }
            }
        }
    }

    private void DrawFoldoutMenu(Tree<RegisteredData> tree)
    {
        if (tree.Nodes.Count != 0)
        {
            EditorGUILayout.BeginVertical();
            foreach (var node in tree.Nodes)
            {
                node.isOpen = EditorGUILayout.Foldout(node.isOpen, node.name);
                if (node.isOpen)
                {
                    EditorGUILayout.BeginHorizontal();
                    GUILayout.Space(10);
                    if (node.Data != null)
                    {
                        EditorGUILayout.BeginVertical();
                        foreach (var gameObject in node.Data.GameObjects)
                        {
                            gameObject.isOpen = EditorGUILayout.Foldout(gameObject.isOpen, gameObject.name);
                            if (gameObject.isOpen)
                            {
                                EditorGUILayout.BeginHorizontal();
                                GUILayout.Space(10);
                                EditorGUILayout.BeginVertical();
                                foreach (var component in gameObject.Components)
                                {
                                    component.isOpen = EditorGUILayout.Foldout(component.isOpen, component.name);
                                    if (component.isOpen)
                                    {
                                        EditorGUILayout.BeginHorizontal();
                                        GUILayout.Space(10);
                                        EditorGUILayout.BeginVertical();
                                        foreach (var field in component.Fields)
                                        {
                                            var value = ValueUtil.Get(field.FieldType);
                                            if (value != null)
                                            {
                                                object info = field.GetValue(component.Instance);
                                                value.LayoutField(field.Name, ref info);
                                                if (field.GetValue(component.Instance) != info)
                                                {
                                                    field.SetValue(component.Instance, info);
                                                }
                                            }
                                        }
                                        EditorGUILayout.EndVertical();
                                        EditorGUILayout.EndHorizontal();
                                    }
                                }
                                EditorGUILayout.EndVertical();
                                EditorGUILayout.EndHorizontal();
                            }
                        }
                        EditorGUILayout.EndVertical();
                    }
                    DrawFoldoutMenu(node);
                    EditorGUILayout.EndHorizontal();
                }
            }
            EditorGUILayout.EndVertical();
        }
        else
        {
            return;
        }
    }
}

[ExecuteAlways]
public class Instance : Singleton<Instance>
{
    public Tree<RegisteredData> tree = new Tree<RegisteredData>("All");
}

public class Node
{
    public string name;
    public bool isOpen = false;
}

public class Tree<T> : Node
{
    public Tree(string n)
    {
        nodes = new List<Tree<T>>();
        name = n;
    }

    public Tree(T data, string n)
    {
        this.Data = data;
        nodes = new List<Tree<T>>();
        name = n;
    }

    private Tree<T> parent;
    /// <summary>
    /// 父结点
    /// </summary>
    public Tree<T> Parent
    {
        get { return parent; }
    }
    /// <summary>
    /// 结点数据
    /// </summary>
    public T Data { get; set; }

    private List<Tree<T>> nodes;
    /// <summary>
    /// 子结点
    /// </summary>
    public List<Tree<T>> Nodes
    {
        get { return nodes; }
    }
    /// <summary>
    /// 添加结点
    /// </summary>
    /// <param name="node">结点</param>
    public void AddNode(Tree<T> node)
    {
        if (!nodes.Contains(node))
        {
            node.parent = this;
            nodes.Add(node);
        }
    }
    /// <summary>
    /// 添加结点
    /// </summary>
    /// <param name="nodes">结点集合</param>
    public void AddNode(List<Tree<T>> nodes)
    {
        foreach (var node in nodes)
        {
            if (!nodes.Contains(node))
            {
                node.parent = this;
                nodes.Add(node);
            }
        }
    }
    /// <summary>
    /// 移除结点
    /// </summary>
    /// <param name="node"></param>
    public void Remove(Tree<T> node)
    {
        if (nodes.Contains(node))
            nodes.Remove(node);
    }
    /// <summary>
    /// 清空结点集合
    /// </summary>
    public void RemoveAll()
    {
        nodes.Clear();
    }

    public bool Contains(string name)
    {
        foreach (var node in nodes)
        {
            if (node.name == name)
            {
                return true;
            }
        }
        return false;
    }

    public Tree<T> GetTree(string path)
    {
        if (path.Contains("/"))
        {
            string[] paths = path.Split('/');
            if (GetTree(paths[0]) == null)
            {
                return null;
            }
            else
            {
                string _path = "";
                for (int i = 1; i < paths.Length; i++)
                {
                    _path += paths[i];
                    if (i + 1 != paths.Length)
                    {
                        _path += "/";
                    }
                }
                return GetTree(paths[0]).GetTree(_path);
            }
        }
        else
        {
            if (path == "")
            {
                return this;
            }
            foreach (var node in nodes)
            {
                if (node.name == path)
                {
                    return node;
                }
            }
            return null;
        }
    }
}

public class RegisteredData : Node
{
    public List<ComponentsInGameObject> GameObjects { get; }

    public RegisteredData()
    {
        GameObjects = new List<ComponentsInGameObject>();
    }

    public void Add(ComponentsInGameObject gameObject)
    {
        if (!GameObjects.Contains(gameObject))
        {
            GameObjects.Add(gameObject);
        }
    }
}

public class ComponentsInGameObject : Node
{
    public List<FieldsInComponent> Components { get; }

    public ComponentsInGameObject(string n)
    {
        Components = new List<FieldsInComponent>();
        name = n;
    }

    public void Add(FieldsInComponent component)
    {
        if (!Components.Contains(component))
        {
            Components.Add(component);
        }
    }
}

public class FieldsInComponent : Node
{
    public List<FieldInfo> Fields { get; }
    public Component Instance { get; }

    public FieldsInComponent(string n, Component instance)
    {
        Fields = new List<FieldInfo>();
        Instance = instance;
        name = n;
    }

    public void Add(FieldInfo field)
    {
        if (!Fields.Contains(field))
        {
            Fields.Add(field);
        }
    }
}